package be.android.forap;

import java.io.File;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.ConnectivityManager;
import android.os.Environment;
import android.text.TextUtils;
import android.widget.Toast;

public class ForAp extends Application {


	private static ForAp instance = null;


	/**
	 * The suggested by Soonil way of keeping a state for the application is good, however it has one weak point - 
	 * there are cases when OS kills the entire application process. Here is the documentation on this - Processes and lifecycles.
	 * Consider a case - your app goes into the background because somebody is calling you (Phone app is in the foreground now). 
	 * In this case && under some other conditions (check the above link for what they could be) the OS may kill 
	 * your application process, including the Application subclass instance. As a result the state is lost.
	 * When you later return to the application, then the OS will restore its activity stack and Application
	 *  subclass instance, however the myState field will be null.
	 *  AFAIK, the only way to guarantee state safety is to use any sort of persisting the state,
	 *  e.g. using a private for the application file or SharedPrefernces (it eventually uses a private for 
	 *  the application file in the internal filesystem).
	 *  
	 *  http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables
	 * @return
	 */
	public static ForAp getInstance() {
		if(instance != null) {
			return instance;
		} else {
			return new ForAp();
		}
	}
	
	public boolean isOnline() {
	    ConnectivityManager cm =
	        (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);

	    return cm.getActiveNetworkInfo() != null && 
	       cm.getActiveNetworkInfo().isConnectedOrConnecting();
	}


	final static private String PREFS_NAME = "FORAP_PREFS";

	final static private String LOGGED_IN = "LOGGED_IN";
	final static private String USER = "USER";
	final static private String PASSWORD = "PASSWORD";
	final static private String USER_PUBLICKEY= "PUBLIC_KEY";
	final static private String USER_PRIVATEKEY= "PRIVATE_KEY";

	final static private String DROPBOX_LINKED= "DROPBOX";
	final static private String ACCESS_KEY_NAME = "ACCESS_KEY";
	final static private String ACCESS_SECRET_NAME = "ACCESS_SECRET";

	private static final String NONCE_NB = "NONCE";

	public void onCreate() {
		super.onCreate();
		instance = this;

		SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		mLogged_in = settings.getBoolean(LOGGED_IN, false);
		mUser = settings.getString(USER, "");
		mPassword = settings.getString(PASSWORD, "");
		mUser_publickey = settings.getString(USER_PUBLICKEY, "");
		mUser_privatekey = settings.getString(USER_PRIVATEKEY, "");

		mDropbox_linked = settings.getBoolean(DROPBOX_LINKED, false);
		mAcces_key_name = settings.getString(ACCESS_KEY_NAME, "");
		mAcces_secret_name = settings.getString(ACCESS_SECRET_NAME, "");
	}

	@Override
	public void onTerminate() {
		instance = null;
		super.onTerminate();
	}


	private boolean mLogged_in;
	private String mUser;
	private String mPassword;
	private String mUser_publickey;
	private String mUser_privatekey;

	public boolean isLoggedIn(){
		return mLogged_in;
	}

	public void login() {
		mLogged_in = true;
		//update prefs
		SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
		Editor edit = prefs.edit();
		edit.putBoolean(LOGGED_IN, true);
		edit.commit();
	}

	public void logout() {
		mLogged_in = false;
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		SharedPreferences.Editor editor = settings.edit();
		editor.putBoolean(LOGGED_IN, false);
		editor.commit();
	}

	
	public Account getForApAccount(){
		Account[] accounts = AccountManager.get(getApplicationContext()).getAccountsByType(Constants.ACCOUNT_TYPE);
		if(accounts != null && accounts.length == 1){
			return accounts[0];
		}else{
			Toast.makeText(getApplicationContext(), "No forap account!", Toast.LENGTH_SHORT).show();
			return null;
		}
	}
	public void createAccount(String uname, String pword, String pubKey, String secKey) {
		mLogged_in = true;
		mUser = uname;
		mPassword = pword;
		mUser_publickey = pubKey;	
		mUser_privatekey = secKey;
		//update prefs
		SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
		Editor edit = prefs.edit();
		edit.putBoolean(LOGGED_IN, true);
		edit.putString(USER, uname);
		edit.putString(PASSWORD, pword);
		edit.putString(USER_PUBLICKEY, pubKey);
		edit.putString(USER_PRIVATEKEY, secKey);
		edit.commit();
		// make folder on sd card
		String state = Environment.getExternalStorageState();
		if (Environment.MEDIA_MOUNTED.equals(state)) {
			File sdCard = Environment.getExternalStorageDirectory();
			final File dir = new File (sdCard.getAbsolutePath() + "/ForAp");
			if(!dir.exists())
				dir.mkdirs();
		}
	}

	public void removeAccount() {
		mLogged_in = false;
		mUser = "";
		mPassword = "";
		mUser_publickey = "";	
		mUser_privatekey = "";
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		SharedPreferences.Editor editor = settings.edit();
		editor.putBoolean(LOGGED_IN, false);
		editor.remove(USER);
		editor.remove(PASSWORD);
		editor.remove(USER_PUBLICKEY);
		editor.remove(USER_PRIVATEKEY);
		//editor.remove(DROPBOX_LINKED);
		editor.commit();
	}

	public String getUser() {
		return mUser;
	}

	public String getPassword() {
		return mPassword;
	}

	public String getPublicKey() {
		return mUser_publickey;
	}

	public String getPrivateKey() {
		return mUser_privatekey;
	}

	private boolean mDropbox_linked;
	private String mAcces_key_name;
	private String mAcces_secret_name;

	public String getAccesKeyName() {
		return mAcces_key_name;
	}

	public String getAccesSecretName() {
		return mAcces_secret_name;
	}

	public void setAccesKeyNameAndSecretName(String acces_key_name,String acces_secret_name) {
		setLinkedToDropbox(true);
		mAcces_key_name = acces_key_name;
		mAcces_secret_name = acces_secret_name;
		//update prefs
		SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
		Editor edit = prefs.edit();
		edit.putString(ACCESS_KEY_NAME, acces_key_name);
		edit.putString(ACCESS_SECRET_NAME, acces_secret_name);
		edit.commit();
	}

	public void unlinkDropbox(){		
		setLinkedToDropbox(false);
		mAcces_key_name = "";
		mAcces_secret_name = "";
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		SharedPreferences.Editor editor = settings.edit();
		editor.remove(ACCESS_KEY_NAME);
		editor.remove(ACCESS_SECRET_NAME);
		editor.commit();
	}

	public boolean isLinkedToDropbox() {
		return mDropbox_linked;
	}

	public void setLinkedToDropbox(boolean dropboxLinked) {
		mDropbox_linked = dropboxLinked;
		//update prefs
		SharedPreferences prefs = getSharedPreferences(PREFS_NAME, 0);
		Editor edit = prefs.edit();
		edit.putBoolean(DROPBOX_LINKED, dropboxLinked);
		edit.commit();
	}

	public byte[] getNonce() throws NoSuchAlgorithmException {
		SharedPreferences settings = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
		String n = settings.getString(NONCE_NB, "");
		byte nonce[] = new byte[16];
		if(TextUtils.isEmpty(n)){			
			SecureRandom gen = SecureRandom.getInstance("SHA1PRNG");
			gen.nextBytes(nonce);
			Editor edit = settings.edit();
			edit.putString(NONCE_NB, new String (nonce));
			edit.commit();
			return nonce;
		}else{
			BigInteger value = new BigInteger(nonce);
			BigInteger sum = value.add(BigInteger.ONE);
			Editor edit = settings.edit();
			edit.putString(NONCE_NB, new String (nonce));
			edit.commit();
			return sum.toByteArray();
		}		
	}
}
